# coding: utf8
'''
Doc:随机数相关的工具函数
    MyRandom:战斗过程的随机数创建方法


Created on 2015年1月27日
@author jpbai
'''
import math
import random
import json
import copy
from collections import OrderedDict
from pyshare.utils.list_utils import many_element_to_weighted_list


class FightRandom(object):
    """线性同余方法产生随机数"""
    # Numerical Recipes的配置参数
    a = 1664525
    c = 1013904223
    m = math.pow(2, 32)

    def __init__(self, seed=1):
        self.seed = seed

    def randint(self, max=None):
        rand = (self.seed * FightRandom.a + FightRandom.c) % FightRandom.m

        rand = int(rand)

        self.seed = rand

        if max is not None:
            rand = rand % max + 1

        return rand

    def ratioJudgement(self, ratio):
        '''判断随机事件是否发生'''
        result = False
        if ratio and ratio > 0:
            if ratio >= 100:
                result = True

            if self.randint(100) <= ratio:
                result = True
        return result


def simple_choice(ratio):
    """
    简易判断事件是否发生
    :param ratio: 概率
    :return:
    """
    result = False
    if ratio and ratio > 0:
        if ratio >= 100:
            result = True

        if random.randint(1, 100) <= ratio:
            result = True
    return result


def weighted_choice(weights):
    """
    带权重的随机获取元素
    :param weights:格式为'元素:权重'的权重字典
    e.g:d = {
        'A': 5,
        'B': 10,
        'C': 15,
        'D': 20,
        'E': 25,
        'F': 30,
        'G': 35,
        'H': 40,
        'I': 45,
        'J': 50,
        'k': 55,
        'L': 60,
        'M': 65,
        'N': 70,
        'O': 75,
        'P': 80,
    }
    :return: 根据权重返回一个随机元素
    """
    totals = sum(weights.values())

    rand = random.uniform(0, totals)
    upto = 0
    for e, w in weights.iteritems():
        if upto + w > rand:
            return e
        upto += w
    assert False, '到这里就错了'


def weighted_choice_key(weights):
    """
    带权重的随机获取元素
    :param weights:格式为'权重:元素'的权重字典
    e.g:d = {
        5: "A",
        10: "B",
        15: "C",
        20: "D",
        25: "E",
        30: "F",
        35: "G",
        40: "H",
        45: "I",
        50: "J",
        55: "k",
        60: "L",
        65: "M",
        70: "N",
        75: "O",
        80: "P"
    }
    :return: 根据权重返回一个随机元素
    """
    weights_keys = weights.keys()
    weights_keys = [int(w) for w in weights_keys]
    totals = sum(weights_keys)

    rand = random.uniform(0, totals)
    upto = 0
    for w, e in weights.iteritems():
        w = int(w)
        if upto + w > rand:
            return e
        upto += w
    assert False, '到这里就错了'


def choose_by_weighted(list, weights, unique=True):
    """
    根据weights随机返回N个list中的元素
    :param list:
    :param weights:权重字典 随机个数的权重
    :param unique: 元素是否不重复
    :return:
    """

    getItemList = []
    if not list:
        return getItemList

    itemNum = int(weighted_choice_key(weights))

    if unique:
        itemNum = itemNum if itemNum <= len(list) else len(list)
        return random.sample(list, itemNum)

    for x in range(1, itemNum + 1):
        getItemList.append(random.choice(list))
    return getItemList


def choose_many_by_random(data_list, num=1, repeat=True):
    """
    从 data_list 中随机选取 num个 元素
    :param data_list: 随机池列表
    :param num: 选取个数
    :param repeat: 是否重复
    :return:
    """
    if not data_list:
        return data_list

    p_list = list()
    if repeat:
        for x in xrange(num):
            p_list.append(data_list[random.randint(0, len(data_list) - 1)])
    else:
        p_list = random.sample(data_list, num)
    return p_list


def random_pick(some_list, probabilities):
    """
    指定概率获取元素
    :param some_list: 元素列表
    :param probabilities: 元素对应的概率列表

    eg. ["a", "b"] [0.2, 0.8] 获取a的概率为20%, b为80%
    """
    x = random.uniform(0, 1)
    cumulative_probability = 0.0
    for item, item_probability in zip(some_list, probabilities):
        cumulative_probability += item_probability
        if x < cumulative_probability:
            break
    return item


def random_weight_pick(sequence, relative_odds):
    """
    根据指定权重获取元素

    :param sequence: 元素序列
    :param relative_odds: 正整数的权重序列

    eg. ["a", "b"] [1, 4] 获取a的概率为20%, b为80%
    """
    table = [z for x, y in zip(sequence, relative_odds) for z in [x] * y]
    return random.choice(table)


def complete_random_list_by_weight(random_cfg_list, random_count=1, element_size=4):
    """
    带权重的列表配置 [*args, 权重]
    根据随机次数(random_count)进行相应次数的完全随机，每次随机不会影响下一次随机
    :param random_cfg_list: 随机列表配置
    :param random_count: 随机次数
    :param element_size: 元素大小
    :return: result_list 随机配置列表[*args, ...]
    """
    for cfg in random_cfg_list:
        if not isinstance(cfg, list) or len(cfg) != element_size:
            return False, "奖励配置数据出错"
    result_list = []
    weights = OrderedDict((json.dumps(cfg[:-1]), cfg[-1]) for cfg in random_cfg_list)
    # 权重列表配置
    for _ in xrange(random_count):
        j_cfg = weighted_choice(weights)
        result_list.append(json.loads(j_cfg))
    return True, result_list


def complete_random_list_by_percent(random_cfg_list, list_nest_num=2, element_size=5, random_value=0):
    """
    百分比随机
    :param random_cfg_list:
    :param list_nest_num: 列表嵌套数量（几维列表）
    :param element_size: 元素大小
    :param random_value: 随机值（可从上层传入）
    :return: 
    """
    result_list = []
    if list_nest_num == 2:
        """[[[*args, percent_start, percent_end], ...], []]"""
        for cfg_list in random_cfg_list:
            # 获取随机数
            truly_random_value = random.randint(1, 100)
            if random_value != 0:
                truly_random_value = random_value
            for element in cfg_list:
                if not isinstance(element, list) or len(element) != element_size:
                    return False, "配置数据出错"
                if element[element_size-2] <= truly_random_value <= element[element_size-1]:
                    result_list.append(element[0:element_size-2])
                    break
    elif list_nest_num == 1:
        """[[*args, percent_start, percent_end], ...]"""
        # 获取随机数
        truly_random_value = random.randint(1, 100)
        if random_value != 0:
            truly_random_value = random_value
        for element in random_cfg_list:
            if not isinstance(element, list) or len(element) != element_size:
                return False, "配置数据出错"
            if element[element_size - 2] <= truly_random_value <= element[element_size - 1]:
                result_list.append(element[0:element_size - 2])
                break

    return True, result_list


def random_weight_pick_many_not_repeat(random_list, pick_count=4):
    """
    根据权重从list中取出pick_count个不重复的元素
    eg. [[31,1,1,5],[31,2,1,25],[31,3,1,25],[31,4,1,25],[31,5,1,25]] => [31,1,1],[31,2,1],[31,3,1],[31,4,1]
    :param random_list: 带权重的列表配置 [*args, 权重]
    :param pick_count: 随机个数
    :return:
    """
    if len(random_list) <= pick_count:
        result_list = [c[:-1] for c in random_list]
    else:
        costume_list = copy.deepcopy(random_list)
        result_list = list()
        for i in range(0, pick_count):
            sequence, relative_odds = many_element_to_weighted_list(costume_list)
            commodity = random_weight_pick(sequence, relative_odds)
            result_list.append(commodity)
            index = sequence.index(commodity)
            costume_list.pop(index)
    return result_list


def random_weight_pick_from_weight_list(random_list):
    """
    从多个权重列表中挑选各1个
    :param random_list: [[[Type,Id, Num, Weight],[Type,Id, Num, Weight]], [Type,Id, Num, Weight]]
    :return:
    """
    result_list = []
    for weight in random_list:
        result = random_weight_pick_many_not_repeat(weight, pick_count=1)
        result_list.extend(result)
    return result_list


def random_weight_pick_not_put_back(random_list, pick_count=1):
    """
    抽取不放回
    根据权重从list中取出pick_count个不重复的元素
    eg. [[31,1,1,5],[31,2,1,25],[31,3,1,25],[31,4,1,25],[31,5,1,25]] => [[31,1,1],[31,2,1],[31,3,1],[31,4,1]]
    :param random_list: 带权重的列表配置 [*args, 权重]
    :param pick_count: 随机个数
    :return:
    """
    # result_list:抽取的数据
    # remain_list:抽完后剩余的数据
    result_list, remain_list = list(), list()

    if len(random_list) <= pick_count:
        result_list = [c[:-1] for c in random_list]
    else:
        costume_list = copy.deepcopy(random_list)
        for i in range(0, pick_count):
            sequence, relative_odds = many_element_to_weighted_list(costume_list)
            commodity = random_weight_pick(sequence, relative_odds)
            result_list.append(commodity)
            index = sequence.index(commodity)
            costume_list.pop(index)
        remain_list = costume_list
    return result_list, remain_list


def choice_by_base_num(possible, base_num):
    """
    判断事件是否发生
    possible=1, base_num=1000 - 千分之一概率触发
    :param possible:
    :param base_num:
    :return:
    """
    rand_num = random.randint(1, base_num)
    if rand_num <= possible:
        return True
    return False


def choice_by_weight_and_rand(weight, rand):
    """
    权重挑选
    e.g: [[1,1],[2,1],[3,1],[4,8],[5,8],[6,8]]
         [[元素, 权重], []]
    :param weight: 权重列表
    :param rand: FightRandom实例
    :return:
    """
    weight_list = [w[1] for w in weight]
    totals = sum(weight_list)

    rand_num = rand.randint(max=totals)-1
    # rand_num范围:[0, totals)
    upto = 0
    for e, w in weight:
        if upto + w > rand_num:
            return e
        upto += w
    assert False, '到这里就错了'


if __name__ == '__main__':
    item_list = [1, 2, 3, 4, 5]
    chance_list = {'50': 0, '30': 1, "20": 2}
    print choose_by_weighted(item_list, chance_list)
    cfg = [[21,1,1,1,30],[21,2,1,31,40]]
    award = complete_random_list_by_percent(cfg, list_nest_num=1, element_size=5)
    print award
    # d = {
    #     "5": "A",
    #     10: "B",
    #     15: "C",
    #     20: "D",
    #     25: "E",
    #     30: "F",
    #     35: "G",
    #     40: "H",
    #     45: "I",
    #     50: "J",
    #     55: "k",
    #     60: "L",
    #     65: "M",
    #     70: "N",
    #     75: "O",
    #     80: "P"
    # }
    # t = {}
    # repeat_time = 0
    # last_key = ''
    # for i in xrange(30):
    #     result = weighted_choice_key(d)
    #     print i, result
    #     if result == last_key:
    #         repeat_time += 1
    #     else:
    #         last_key = result
    #     if not t.has_key(result):
    #         t[result] = 0
    #     t[result] += 1
    # print("========t======", repeat_time, t)

    # import random
    #
    # myRandom = FightRandom(100)
    #
    # randResult = [(myRandom.randint(100)) for i in xrange(1, 100)]
    #
    # min, max = 50, 50
    # for num in randResult:
    #     if num < min:
    #         min = num
    #     if num > max:
    #         max = num
    #
    # print(randResult)
    # print(min, max)
    #
    # randResult = [(myRandom.ratioJudgement(random.randint(1, 100))) for i in xrange(1, 100)]
    # print(randResult)
